<!--
 * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available.
 *
 * Copyright (C) 2021 THL A29 Limited, a Tencent company.  All rights reserved.
 *
 * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License.
 *
 * License for BK-JOB蓝鲸智云作业平台:
 *
 *
 * Terms of the MIT License:
 * ---------------------------------------------------
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
-->

<template>
    <div class="sync-plan-batch-page">
        <list-action-layout>
            <template #right>
                <bk-button
                    theme="primary"
                    :loading="isConfirmLoading || isCronJobLoading"
                    :disabled="planConfirmInfo.unconfirmed < 1 || isComfirmAllFinished"
                    @click="handleConfirmAll">
                    {{ $t('template.全部确认') }}
                </bk-button>
            </template>
        </list-action-layout>
        <div class="render-list-header">
            <div class="list-name">
                <span>{{ $t('template.同步执行方案') }}</span>
                <span class="total">（{{ $t('template.共') }} {{ data.length }} {{ $t('template.个.total') }}）</span>
            </div>
            <div class="item-status">
                <template v-if="isCronJobLoading">
                    <Icon type="sync-pending" svg class="plan-cron-job-loading" />
                </template>
                <template v-else>
                    <span class="confirmed">{{ planConfirmInfo.confirmed }}</span>{{ $t('template.个已就绪') }} ，
                    <span class="unconfirmed">{{ planConfirmInfo.unconfirmed }}</span>{{ $t('template.个未就绪') }}
                </template>
            </div>
        </div>
        <div ref="list">
            <bk-table
                v-if="tableHeight"
                class="sync-plan-list"
                :data="data"
                :max-height="tableHeight"
                :row-class-name="calcRowClass"
                selectable>
                <bk-table-column
                    :label="$t('template.执行方案.colHead')"
                    prop="name"
                    key="name"
                    align="left">
                    <template slot-scope="{ row }">
                        <auth-router-link
                            :permission="row.canView"
                            auth="job_plan/view"
                            :resource-id="row.id"
                            target="_blank"
                            :to="{
                                name: 'viewPlan',
                                params: {
                                    templateId: row.templateId,
                                },
                                query: {
                                    viewPlanId: row.id,
                                },
                            }">
                            {{ row.name }}
                            <Icon type="edit" class="open-link-flag" />
                        </auth-router-link>
                    </template>
                </bk-table-column>
                <bk-table-column
                    :label="$t('template.所属作业模版')"
                    prop="templateName"
                    key="templateName"
                    align="left">
                    <template slot-scope="{ row }">
                        <router-link
                            target="_blank"
                            :to="{
                                name: 'templateDetail',
                                params: {
                                    id: row.templateId,
                                },
                            }">
                            {{ row.templateName }}
                            <Icon type="edit" class="open-link-flag" />
                        </router-link>
                    </template>
                </bk-table-column>
                <bk-table-column
                    :label="$t('template.状态')"
                    prop="statusText"
                    key="statusText2"
                    class-name="status-column"
                    align="left">
                    <template slot-scope="{ row }">
                        <div class="confirm-status-box">
                            <Icon :type="row.statusIcon" svg class="status-flag" :class="row.statusIcon" />
                            <span v-html="row.statusHtml" />
                            <bk-button
                                v-if="row.isRetryEnable"
                                text
                                class="ml10"
                                @click="handleSyncRetry(row)">
                                {{ $t('template.重试') }}
                            </bk-button>
                        </div>
                    </template>
                </bk-table-column>
                <bk-table-column
                    :resizable="false"
                    :label="$t('template.操作')"
                    width="280"
                    key="action"
                    align="left">
                    <template slot-scope="{ row }">
                        <span :tippy-tips="row.disableDiffTips" class="mr10">
                            <bk-button
                                :disabled="!!row.disableDiffTips"
                                text
                                @click="handleGoDiff(row)">
                                {{ $t('template.查看差异') }}
                            </bk-button>
                        </span>
                        <!-- 定时任务加载中 -->
                        <template v-if="row.isCronJobLoading">
                            <Icon type="sync-pending" svg class="plan-cron-job-loading" />
                        </template>
                        <template v-else>
                            <span :tippy-tips="row.disableConfirmTips" class="mr10">
                                <bk-button
                                    text
                                    :loading="row.isCronJobLoading"
                                    :disabled="!!row.disableConfirmTips"
                                    @click="handleConfirmCron(row)">
                                    {{ $t('template.确认定时任务') }}
                                </bk-button>
                            </span>
                            <div
                                class="confirm-status"
                                :class="{ confirmed: row.isConfirmed }">
                                {{ row.confirmProcessText }}
                            </div>
                        </template>
                    </template>
                </bk-table-column>
            </bk-table>
        </div>
        <div class="sync-plan-action">
            <bk-button v-if="isFinished" class="w120" theme="primary" @click="handleFinish">
                {{ $t('template.完成') }}
            </bk-button>
            <template v-else>
                <bk-button class="mr10" @click="handleCancle">
                    {{ $t('template.取消') }}
                </bk-button>
                <span :tippy-tips="syncSubmitInvalid ? $t('template.所有方案均已同步至最新版') : ''">
                    <bk-button
                        class="w120"
                        theme="primary"
                        :loading="isSyncLoading || isCronJobLoading"
                        :disabled="syncSubmitInvalid || planConfirmInfo.unconfirmed > 0"
                        @click="handleSubmitSync">
                        {{ $t('template.立即同步') }}
                    </bk-button>
                </span>
            </template>
        </div>
        <confirm-cron
            :is-show="isShowConfirmCron"
            v-bind="selectPlanInfo"
            @on-change="handleSelectPlanConfirmChange"
            @on-close="hanndleSelectPlanConfirmClose" />
    </div>
</template>
<script>
    import _ from 'lodash';
    import I18n from '@/i18n';
    import TaskManageService from '@service/task-manage';
    import TaskPlanService from '@service/task-plan';
    import TimeTaskService from '@service/time-task';
    import SyncPlanVO from '@domain/variable-object/sync-plan';
    import ListActionLayout from '@components/list-action-layout';
    import {
        getOffset,
        leaveConfirm,
    } from '@utils/assist';
    import ConfirmCron from './components/confirm-cron';

    const runStepByStep = (data, callback, finishCallback = () => {}) => {
        let startIndex = 0;
        const next = () => {
            startIndex += 1;
            if (startIndex >= data.length) {
                finishCallback();
                return;
            }
            callback(data[startIndex], next);
        };
        callback(data[startIndex], next);
    };

    export default {
        name: '',
        components: {
            ListActionLayout,
            ConfirmCron,
        },
        data () {
            return {
                isLoading: true,
                data: [],
                tableHeight: '',
                isCronJobLoading: false,
                isConfirmLoading: false,
                isComfirmAllFinished: false,
                isSyncLoading: false,
                isShowConfirmCron: false,
                isFinished: false,
                syncValueMemoMap: {},
                selectPlanInfo: {
                    templateId: -1,
                    planId: -1,
                    cronJobInfoList: [],
                },
            };
        },
        computed: {
            isSkeletonLoading () {
                return this.isLoading;
            },
            planConfirmInfo () {
                let confirmed = 0;
                let unconfirmed = 0;
                
                this.data.forEach((currentPlan) => {
                    if (currentPlan.isConfirmed) {
                        confirmed += 1;
                    } else {
                        unconfirmed += 1;
                    }
                });
                return {
                    confirmed,
                    unconfirmed,
                };
            },
            syncSubmitInvalid () {
                // eslint-disable-next-line no-plusplus
                for (let i = 0; i < this.data.length; i++) {
                    const currentPlan = this.data[i];
                    // 需要确认定时任务
                    if (currentPlan.needUpdate) {
                        return false;
                    }
                }
                return true;
            },
        },
        created () {
            const { planIds = '' } = this.$route.query;
            this.planIds = planIds;
            // 如果是从作业模版的执行方案列表过来同步的
            // 所有执行方案的templateId相同，保留下来，路由回退时需要
            this.lastOnePlanTemplateId = '';
            this.fetchData();
        },
        mounted () {
            this.calcTableHeight();
        },
        methods: {
            /**
             * @desc 获取要同步的执行方案基本信息
             *
             * 初始化同步执行方案的状态
             */
            fetchData () {
                this.isLoading = true;
                TaskPlanService.fetchBatchPlan({
                    planIds: this.planIds,
                }).then((data) => {
                    const planData = [];
                    const needCheckCronJobStatusPlanMap = {};
                    
                    data.forEach((_) => {
                        const currentSyncPlan = new SyncPlanVO(_);
                        planData.push(currentSyncPlan);
                        this.lastOnePlanTemplateId = _.templateId;
                        // 执行方案有关联定时任务
                        // 执行方案的状态设置为加载定时任务中（SyncPlanVO.STATUS_CRON_JOB_LOADING）
                        // 缓存该执行方案id用于下一步获取关联定时任务
                        if (currentSyncPlan.cronJobCount > 0) {
                            currentSyncPlan.status = SyncPlanVO.STATUS_CRON_JOB_LOADING;
                            needCheckCronJobStatusPlanMap[currentSyncPlan.id] = currentSyncPlan;
                        }
                    });
                    this.data = planData;

                    // 有执行方案需要获取关联定时任务
                    // 页面操作状态设置定时任务任务获取中（isCronJobLoading），其它操作失效
                    const needCheckCronJobStatusPlanIds = Object.keys(needCheckCronJobStatusPlanMap);
                    if (needCheckCronJobStatusPlanIds.length > 0) {
                        this.isCronJobLoading = true;
                        TimeTaskService.fetchTaskOfPlanBatch({
                            planIds: needCheckCronJobStatusPlanIds.join(','),
                        }).then((data) => {
                            for (const planId in data) {
                                const currentCronJobList = data[planId];
                                if (currentCronJobList.some(_ => _.enable)) {
                                    // 执行方案关联的定时任务[有开启状态]——同步状态设置为未就绪
                                    needCheckCronJobStatusPlanMap[planId].status = SyncPlanVO.STATUS_DEFAULT;
                                } else {
                                    // 执行方案关联的定时任务[全部是关闭状态]——同步状态设置为已就绪
                                    needCheckCronJobStatusPlanMap[planId].status = SyncPlanVO.STATUS_CONFIRMED;
                                }
                            }
                        })
                            .finally(() => {
                                this.isCronJobLoading = false;
                            });
                    }
                    
                    if (this.data.length > 0) {
                        window.changeAlert = true;
                    }
                })
                    .finally(() => {
                        this.isLoading = false;
                    });
            },

            /**
             * @desc 计算页面高度，实现表格内部滚动
             */
            calcTableHeight () {
                const { top } = getOffset(this.$refs.list);
                const windowHeight = window.innerHeight;
                this.tableHeight = windowHeight - top - 77;
            },
            /**
             * @desc 计算表格行的样式
             */
            calcRowClass ({ row }) {
                let className = 'template-plan-sync-record';
                if (!row.canEdit) {
                    className = `${className} sync-permission`;
                }
                return className;
            },
            /**
             * @desc 全部确认操作
             *
             * 一条条数据串联同步
             */
            handleConfirmAll () {
                window.changeAlert = true;
                this.isConfirmLoading = true;
                const syncValueMemoMap = { ...this.syncValueMemoMap };
                this.data.forEach((item) => {
                    if (syncValueMemoMap[item.id]) {
                        return;
                    }
                    item.status = SyncPlanVO.STATUS_CONFIRM_QUEUE;
                });
                let errorNums = 0;
                const confirmOnePlan = (plan, next) => {
                    const currentSyncPlan = plan;
                    const syncValue = {
                        planId: currentSyncPlan.id,
                        templateId: currentSyncPlan.templateId,
                        templateVersion: currentSyncPlan.templateVersion,
                        cronJobInfoList: [],
                    };
                    
                    // 执行方案没有查看和编辑权限——跳过
                    if (!currentSyncPlan.canView || !currentSyncPlan.canEdit) {
                        next();
                        return;
                    }
                    // 已确认——跳过
                    if (currentSyncPlan.isConfirmed) {
                        next();
                        return;
                    }
                    // 没有定时任务——跳过
                    if (currentSyncPlan.isPassConfirm) {
                        syncValueMemoMap[currentSyncPlan.id] = syncValue;
                        next();
                        return;
                    }
                    
                    // 定时任务确认中
                    currentSyncPlan.status = SyncPlanVO.STATUS_CONFIRM_PENDGING;
                    Promise.all([
                        TaskManageService.taskDetail({
                            id: currentSyncPlan.templateId,
                        }),
                        TimeTaskService.fetchTaskOfPlan({
                            id: currentSyncPlan.id,
                        }),
                    ]).then(([
                        template,
                        cronJobList,
                    ]) => {
                        // 作业模版中的变量
                        const currentTemplateVariableList = template.variables;

                        // 必填变量没有赋值
                        let isRequiredError = false;
                        let isPermissionError = false;

                        // 确认定时任务的变量值
                        // 1，将模版中的变量同步到定时任务中
                        // 2，作业模版和定时任务同名的变量保留定时任务中的变量值
                        // 3，作业模版中新增的变量为必填但值为空则同步失败
                        const cronJobInfoList = [];
                        // eslint-disable-next-line no-plusplus
                        for (let i = 0; i < cronJobList.length; i++) {
                            const currentCronJob = cronJobList[i];

                            const currentCronJobInfo = {
                                id: currentCronJob.id,
                                name: currentCronJob.name,
                                enable: currentCronJob.enable,
                                hasConfirm: false,
                                variableValue: [],
                            };
                            // 没有定时任务的管理权限——跳过处理
                            if (!currentCronJob.canManage) {
                                isPermissionError = true;
                                cronJobInfoList.push(currentCronJobInfo);
                                continue;
                            }
                            // 当前定时任务未被开启——跳过处理
                            if (!currentCronJob.enable) {
                                cronJobInfoList.push(currentCronJobInfo);
                                continue;
                            }

                            // 同步作业模版中变量到定时任务
                            // 作业模版和定时任务同名的变量——保留定时任务中的变量值
                            const currentCronJobVariableMap = currentCronJob.variableValue.reduce((result, item) => {
                                result[item.id] = item;
                                return result;
                            }, {});
                            const newCronJobVariableList = [];
                            // eslint-disable-next-line no-plusplus
                            for (let j = 0; j < currentTemplateVariableList.length; j++) {
                                const newVariableFromTemplate = _.cloneDeep(currentTemplateVariableList[j]);
                                
                                if (currentCronJobVariableMap[newVariableFromTemplate.id]) {
                                    const {
                                        value,
                                        targetValue,
                                    } = currentCronJobVariableMap[newVariableFromTemplate.id];
                                    newVariableFromTemplate.defaultValue = value;
                                    newVariableFromTemplate.defaultTargetValue = targetValue;
                                }
                                // 必填变量不能为空
                                if (newVariableFromTemplate.isRequired && newVariableFromTemplate.isEmpty) {
                                    isRequiredError = true;
                                }
                                const { id, name, type, defaultValue, defaultTargetValue } = newVariableFromTemplate;
                                // 定时任务中的变量需要赋值操作
                                newCronJobVariableList.push({
                                    id,
                                    name,
                                    type,
                                    defaultValue,
                                    defaultTargetValue,
                                    value: defaultValue,
                                    targetValue: defaultTargetValue,
                                });
                            }

                            // 有必填变量没有被赋值，确实失败
                            currentCronJobInfo.hasConfirm = !isRequiredError;
                            currentCronJobInfo.variableValue = newCronJobVariableList;
                            cronJobInfoList.push(currentCronJobInfo);
                        }
                        
                        // 手动确认过——继续使用手动确认的结果
                        if (!syncValueMemoMap[currentSyncPlan.id]) {
                            syncValue.cronJobInfoList = cronJobInfoList;
                            syncValueMemoMap[currentSyncPlan.id] = syncValue;
                        }

                        // 定时任务确认完成
                        currentSyncPlan.cronJobInfoList = cronJobInfoList;
                        if (isRequiredError) {
                            currentSyncPlan.status = SyncPlanVO.STATUS_CONFIRM_FAILED;
                            currentSyncPlan.error = I18n.t('template.定时任务中必填变量未赋值');
                            errorNums += 1;
                        } else if (isPermissionError) {
                            currentSyncPlan.status = SyncPlanVO.STATUS_CONFIRM_FAILED;
                            currentSyncPlan.error = I18n.t('template.没有定时任务管理权限，请手动确认');
                            errorNums += 1;
                        } else {
                            currentSyncPlan.status = SyncPlanVO.STATUS_CONFIRMED;
                        }
                    })
                        .catch(() => {
                            // 定时任务确认失败
                            currentSyncPlan.status = SyncPlanVO.STATUS_CONFIRM_FAILED;
                            currentSyncPlan.error = I18n.t('template.自动确认定时任务失败，请手动确认');
                            errorNums += 1;
                        })
                        .finally(() => {
                            next();
                        });
                };
                
                runStepByStep(this.data, confirmOnePlan, () => {
                    this.isConfirmLoading = false;
                    this.syncValueMemoMap = Object.freeze(syncValueMemoMap);
                    this.isComfirmAllFinished = true;
                    if (errorNums > 0) {
                        this.messageError(`${errorNums} ${I18n.t('template.项执行方案的确认出现问题，请逐个确认')}`);
                    }
                });
            },
            /**
             * @desc 查看同步差异
             */
            handleGoDiff (plan) {
                const router = this.$router.resolve({
                    name: 'syncPlan',
                    params: {
                        id: plan.id,
                        templateId: plan.templateId,
                    },
                    query: {
                        mode: 'view',
                    },
                });
                window.open(router.href);
            },
            /**
             * @desc 打开手动确认弹框
             * @param {Object} plan 要确认的执行方案
             */
            handleConfirmCron (plan) {
                let cronJobInfoList = [];
                if (this.syncValueMemoMap[plan.id]) {
                    /* eslint-disable prefer-destructuring */
                    cronJobInfoList = this.syncValueMemoMap[plan.id].cronJobInfoList;
                }
                this.isShowConfirmCron = true;
                this.selectPlanInfo = {
                    templateId: plan.templateId,
                    planId: plan.id,
                    cronJobInfoList,
                };
            },
            /**
             * @desc 关闭手动确认弹框
             */
            hanndleSelectPlanConfirmClose () {
                this.isShowConfirmCron = false;
                this.selectPlanInfo = {
                    templateId: -1,
                    planId: -1,
                    cronJobInfoList: [],
                };
            },
            /**
             * @desc 提交手动确认的定任务信息
             * @param {Array} cronJobInfoList 执行方案关联的定时任务变量信息
             */
            handleSelectPlanConfirmChange (cronJobInfoList) {
                window.changeAlert = true;

                const syncValueMemoMap = { ...this.syncValueMemoMap };
                
                syncValueMemoMap[this.selectPlanInfo.planId] = {
                    templateId: this.selectPlanInfo.templateId,
                    id: this.selectPlanInfo.planId,
                    cronJobInfoList,
                };
                this.syncValueMemoMap = Object.freeze(syncValueMemoMap);

                const currentPlan = _.find(this.data, _ => _.id === this.selectPlanInfo.planId);
                // 定时任务全部确认完成
                if (cronJobInfoList.every(_ => _.hasConfirm || !_.enable)) {
                    currentPlan.status = SyncPlanVO.STATUS_CONFIRMED;
                }
                currentPlan.cronJobInfoList = cronJobInfoList;
                this.data = [
                    ...this.data,
                ];
            },
            /**
             * @desc 提交同步
             *
             * 一条一条数据串联同步
             */
            handleSubmitSync () {
                this.isSyncLoading = true;
                this.data.forEach((item) => {
                    // 进入同步队列
                    if (item.needUpdate) {
                        item.status = SyncPlanVO.STATUS_SYNC_QUEUE;
                    }
                });
                
                const syncOnePlan = (plan, next) => {
                    const currentSyncPlan = plan;
                    // 需要同步的执行方案才会变更同步状态
                    if (!currentSyncPlan.needUpdate) {
                        currentSyncPlan.status = SyncPlanVO.STATUS_SYNCED;
                        next();
                        return;
                    }
                    
                    // 同步中
                    currentSyncPlan.status = SyncPlanVO.STATUS_SYNC_PENDING;

                    TaskPlanService.planSyncInfo({
                        planId: currentSyncPlan.id,
                        templateId: currentSyncPlan.templateId,
                        templateVersion: currentSyncPlan.templateVersion,
                    }).then(() => {
                        // 有定时任务才会执行同步定时任务
                        if (this.syncValueMemoMap[currentSyncPlan.id]
                            && this.syncValueMemoMap[currentSyncPlan.id].cronJobInfoList.length > 0) {
                            return TimeTaskService.updatePlanTask({
                                cronJobInfoList: this.syncValueMemoMap[currentSyncPlan.id].cronJobInfoList,
                            });
                        }
                        // 不需要同步定时任务
                        return Promise.resolve();
                    })
                        .then(() => {
                            // 同步成功
                            currentSyncPlan.status = SyncPlanVO.STATUS_SYNCED;
                        })
                        .catch(() => {
                            // 同步失败
                            currentSyncPlan.status = SyncPlanVO.STATUS_SYNC_FAILED;
                        })
                        .finally(() => {
                            next();
                        });
                };

                runStepByStep(this.data, syncOnePlan, () => {
                    this.isSyncLoading = false;
                    this.isFinished = true;
                    window.changeAlert = false;
                });
            },
            /**
             * @desc 同步失败重试
             * @param {Object} plan 重试的执行方案
             */
            handleSyncRetry (plan) {
                plan.status = SyncPlanVO.STATUS_SYNC_PENDING;
                TaskPlanService.planSyncInfo({
                    planId: plan.id,
                    templateId: plan.templateId,
                    templateVersion: plan.templateVersion,
                }).then(() => TimeTaskService.updatePlanTask({
                    cronJobInfoList: this.syncValueMemoMap[plan.id].cronJobList,
                }))
                    .then(() => {
                        // 同步成功
                        plan.status = SyncPlanVO.STATUS_SYNCED;
                    })
                    .catch(() => {
                        // 同步失败
                        plan.error = I18n.t('template.同步请求失败，请重试');
                        plan.status = SyncPlanVO.STATUS_SYNC_FAILED;
                    });
            },
            /**
             * @desc 取消批量同步
             *
             * 需要确认页面的编辑状态
             */
            handleCancle () {
                leaveConfirm()
                    .then(() => {
                        this.routerBack();
                    });
            },
            /**
             * @desc 完成批量同步
             */
            handleFinish () {
                window.changeAlert = false;
                this.routerBack();
            },
            /**
             * @desc 路由回退
             */
            routerBack () {
                const { from } = this.$route.query;
                if (from === 'viewPlan') {
                    this.$router.push({
                        name: 'viewPlan',
                        params: {
                            templateId: this.lastOnePlanTemplateId,
                        },
                    });
                } else if (from === 'planList') {
                    this.$router.push({
                        name: 'planList',
                    });
                } else if (from === 'templateDetail') {
                    this.$router.push({
                        name: 'templateDetail',
                        params: {
                            id: this.lastOnePlanTemplateId,
                        },
                    });
                } else if (from === 'templateEdit') {
                    this.$router.push({
                        name: 'viewPlan',
                        params: {
                            templateId: this.lastOnePlanTemplateId,
                        },
                    });
                } else {
                    this.$router.push({
                        name: 'planList',
                    });
                }
            },
        },
    };
</script>
<style lang='postcss'>
    @keyframes sync-loading-ani {
        from {
            transform: rotateZ(0);
        }

        to {
            transform: rotateZ(360deg);
        }
    }

    .sync-plan-batch-page {
        .render-list-header {
            display: flex;
            height: 42px;
            padding: 0 15px;
            font-size: 12px;
            color: #63656e;
            background: #f0f1f5;
            border: 1px solid #dcdee5;
            border-bottom: none;
            align-items: center;

            .list-name {
                font-weight: bold;

                .total {
                    color: #979ba5;
                }
            }

            .item-status {
                margin-left: auto;

                .confirmed,
                .unconfirmed {
                    padding-right: 4px;
                    font-weight: bold;
                }

                .confirmed {
                    color: #3a84ff;
                }
            }
        }

        .sync-plan-list {
            &.bk-table {
                border-top-right-radius: 0;
                border-top-left-radius: 0;
            }

            .template-plan-sync-record {
                &:hover {
                    .open-link-flag {
                        opacity: 1;
                    }
                }
            }

            .sync-permission {
                background: #fafbfd;
            }

            .open-link-flag {
                font-size: 12px;
                opacity: 0;
            }

            .status-column {
                .cell {
                    overflow: unset;
                }
            }

            .confirm-status-box {
                display: flex;
                align-items: center;

                .status-flag {
                    margin-right: 4px;

                    &.sync-default {
                        color: #c4c6cc;
                    }

                    &.sync-pending {
                        color: #3a84ff;
                        animation: sync-loading-ani 1s linear infinite;
                    }

                    &.sync-success {
                        color: #3fc06d;
                    }

                    &.sync-failed {
                        color: #ea3636;
                    }
                }

                span[tippy-tips] {
                    padding-bottom: 2px;
                    cursor: pointer;
                    border-bottom: 1px dashed #c4c6cc;
                }
            }
        }

        .confirm-status {
            height: 16px;
            padding: 0 5px;
            font-size: 12px;
            line-height: 16px;
            color: #979ba5;
            background: #f0f1f5;
            border-radius: 8px;

            &.confirmed {
                color: #3a84ff;
                background: #e1ecff;
            }
        }

        .plan-cron-job-loading {
            color: #3a84ff;
            animation: sync-loading-ani 1s linear infinite;
        }

        .sync-plan-action {
            position: fixed;
            right: 0;
            bottom: 0;
            left: 0;
            display: flex;
            justify-content: flex-end;
            align-items: center;
            height: 52px;
            padding-right: 24px;
            background: #fff;
            box-shadow: 0 -2px 4px 0 rgba(0, 0, 0, 0.06);
        }
    }
</style>
